/*
 * ArrayMultiD.h
 *
 *  Created on: May 5, 2011
 *      Author: snirgaz
 */
#ifndef ARRAYMULTID_H_
#define ARRAYMULTID_H_

#include "def.h"
#include <iterator>
#include <array>
//#include "aligned.h"

using namespace std;

template<typename T, int D>
ostream & operator<<(ostream &cout, array<T, D> &index) {
	for (int d = 0; d < D; d++) {
		cout << index[d] << " ";
	}
	cout << endl;
	return cout;
}

template<class T, int D>
class ArrayMulti {
	typedef T ValueType;
	typedef array<int, D> IndexType;
	typedef vector<T> ContainerType;

protected:
	IndexType sizes_;
	ContainerType data_;
	int totalSize_;
public:
	typedef typename ContainerType::iterator iterator;
	typedef typename ContainerType::const_iterator const_iterator;
	class IterIndex;
	void init(IndexType const &sizes) {
		sizes_ = sizes;
		totalSize_ = accumulate(sizes_.begin(), sizes_.end(), 1,
				multiplies<int>());
		data_.resize(totalSize_);
	}
	void init(IndexType &sizes, T const &initData) {
		sizes_ = sizes;
		totalSize_ = accumulate(sizes_.begin(), sizes_.end(), 1,
				multiplies<int>());
		data_.resize(totalSize_, initData);
	}
	int getPosByIndex(const IndexType &pos) {
		int posOneD = 0;
		for (int d = 0; d < D; ++d) {
			posOneD = posOneD * sizes_[d];
			posOneD += pos[d];
		}
		return posOneD;
	}
	T& operator()(const IndexType &pos) {
		return data_[this->getPosByIndex(pos)];
	}
	T operator()(const IndexType &pos) const {
		return data_[this->getPosByIndex(pos)];
	}
	class IterSlice: public std::iterator<bidirectional_iterator_tag, T> {
	public:
		IterSlice(iterator it, int jump) :
				it_(it), jump_(jump) {
		}
		iterator getIter() {
			return it_;
		}
		IterSlice& operator++() {
			it_ += jump_;
			return *this;
		}
		IterSlice& operator--() {
			it_ -= jump_;
			return *this;
		}
		IterSlice operator++(int) {
			IterSlice temp(*this);
			++(*this);
			return temp;
		}
		IterSlice operator--(int) {
			IterSlice temp(*this);
			--(*this);
			return temp;
		}
		bool operator==(IterSlice const& other) const {
			return this->it_ == other.it_;
		}
		bool operator!=(IterSlice const& other) const {
			return this->it_ != other.it_;
		}
		T& operator*() const {
			return it_.operator*();
		}
		T* operator->() const {
			return it_.operator->();
		}
		iterator it_;
		int jump_;
	};
	class IterIndex: public std::iterator<bidirectional_iterator_tag, T> {
	public:
		IndexType getIndex() {
			return indexPos_;
		}
		IterIndex() {
		}
		;
		IterIndex(iterator it, IndexType indexPos, IndexType sizes) :
				it_(it), indexPos_(indexPos), sizes_(sizes) {
		}
		iterator getIter() {
			return it_;
		}
		IterIndex& operator++() {
			++it_;
			for (int d = D - 1; d >= 0; --d) {
				indexPos_[d]++;
				if (indexPos_[d] == (sizes_[d])) {
					indexPos_[d] = 0;
				} else
					break;
			}
			return *this;
		}
		IterIndex& operator--() {
			--it_;
			for (int d = D - 1; d >= 0; --d) {
				indexPos_[d]--;
				if (indexPos_[d] == -1) {
					indexPos_[d] = (sizes_[d] - 1);
				} else
					break;
			}
			return *this;
		}
		IterIndex operator++(int) {
			IterIndex temp(*this);
			++(*this);
			return temp;
		}
		IterIndex operator--(int) {
			IterIndex temp(*this);
			--(*this);
			return temp;
		}
		bool operator==(IterIndex const& other) const {
			return this->it_ == other.it_;
		}
		bool operator!=(IterIndex const& other) const {
			return this->it_ != other.it_;
		}
		T& operator*() const {
			return it_.operator*();
		}
		T* operator->() const {
			return it_.operator->();
		}
		friend ostream &operator<<(ostream &cout, IterIndex &it) {
			for (int d = 0; d < D; d++) {
				cout << it.indexPos_[d] << " ";
			}
			cout << endl;
			return cout;
		}
		iterator it_;
		IndexType indexPos_;
		IndexType sizes_;
	};
	IterIndex beginIndex() {
		IndexType indexPos;
		fill(indexPos.begin(), indexPos.end(), 0);
		return IterIndex(data_.begin(), indexPos, sizes_);
	}
	IterIndex endIndex() {
		IndexType indexPos;
		transform(sizes_.begin(), sizes_.end(), indexPos.begin(),
				bind2nd(minus<int>(), 1));
		return IterIndex(data_.end(), indexPos, sizes_);
	}
	IterIndex iterIndexByPos(IndexType indexPos) {
		iterator it = data_.begin() + getPosByIndex(indexPos);
		return IterIndex(it, indexPos, sizes_);
	}
	iterator begin() {
		return data_.begin();
	}
	iterator end() {
		return data_.end();
	}
	IterSlice beginSlice(int dim, array<int, D> otherPos) {
		int jump = 0;
		for (int d = 0; d < D; ++d) {
			jump = jump * sizes_[d];
			jump += (d == dim) ? 1 : 0;
		}
		otherPos[dim] = 0;
		return IterSlice(this->iterByPos(otherPos), jump);
	}
	IterSlice endSlice(int dim, array<int, D> otherPos) {
		int jump = 0;
		for (int d = 0; d < D; ++d) {
			jump = jump * sizes_[d];
			jump += (d == dim) ? 1 : 0;
		}
		otherPos[dim] = sizes_[dim] - 1;
		return IterSlice(this->iterByPos(otherPos), jump);
	}
	const_iterator begin() const {
		return data_.begin();
	}
	const_iterator end() const {
		return data_.end();
	}

	T* beginP() {
		return &(data_[0]);
	}
	T* endP() {
		return &(data_[totalSize_ - 1]);
	}
	iterator iterByPos(IndexType indexPos) {
		return data_.begin() + getPosByIndex(indexPos);
	}
	int totalSize() const {
		return totalSize_;
	}
	IndexType posByIter(iterator iter) {
		int size=totalSize_;
		int diff=iter-data_.begin();
		IndexType pos;
		for (int d=0;d<D;d++) {
			size/=sizes_[d];
			pos[d]=diff/size;
			diff=diff-pos[d]*size;
		}
		return pos;
	}

};

#endif //ARRAYMULTID_H_
